#include <iostream>
using namespace std;
template<typename T>
class Matrix2D {
private:
  T *m_data;
  int m_row;
  int m_column;
  int m_size = 0;

public:
  Matrix2D(T *m_data, int row, int col);
  Matrix2D();
  ~Matrix2D();
  int getRow();
  int getColumn();
  int getSize();
  T *getData();
  void setRow(int r);
  void setColumn(int c);
  void setData(int *d);
  void setSize(int s);
  bool illegal(Matrix2D &m);
  Matrix2D &add(Matrix2D &m);
  Matrix2D &sub(Matrix2D &m);
  Matrix2D &dot(Matrix2D &m);
  Matrix2D &div(Matrix2D &m);
  Matrix2D &matmul(Matrix2D &m);
  Matrix2D &scale(float scale,T bias);
  // 拷贝构造函数
  Matrix2D(Matrix2D &);
  // 移动语义
  Matrix2D &operator=(Matrix2D &&) noexcept;
  Matrix2D (initializer_list<T>data,int row,int col);
  Matrix2D &operator+(Matrix2D &m);
  Matrix2D &operator-(Matrix2D &m);
  Matrix2D &operator*(Matrix2D &m);
  Matrix2D &operator/(Matrix2D &m);
  bool operator==(Matrix2D &m);
  friend ostream &operator<<(ostream &os, const Matrix2D<T> &m);
};
template<typename T>
ostream &operator<<(ostream &os, Matrix2D<T> &m) {
  for (int i = 0; i < m.getRow(); i++) {
    for (int j = 0; j < m.getColumn(); j++) {
      cout << m.getData()[i * m.getColumn() + j] << " ";
    }
    cout << "\n";
  }

  return os;
}
template<typename T>
Matrix2D<T>::Matrix2D(T *data, int row, int col)
    : m_data(data), m_row(row), m_column(col) {
  m_size = row * col;
}
template<typename T>
Matrix2D<T>::Matrix2D() : m_data(nullptr), m_row(0), m_column(0), m_size(0) {}
template<typename T>
Matrix2D<T>::~Matrix2D() {
  if (m_data)
    delete[] m_data;
}
template<typename T>
T *Matrix2D<T>::getData() { return m_data; }
template<typename T>
int Matrix2D<T>::getRow() { return m_row; }
template<typename T>
int Matrix2D<T>::getColumn() { return m_column; }
template<typename T>
int Matrix2D<T>::getSize() { return m_size; }
template<typename T>
void Matrix2D<T>::setSize(int s) { m_size = s; }
template<typename T>
Matrix2D<T> &Matrix2D<T>::operator=(Matrix2D &&m) noexcept {
  if (this != &m) {
    if (this->m_data) {
      delete[] m_data;
      m_data = m.getData();
      m_column = m.getColumn();
      m_row = m.getRow();
    }
  }
  return *this;
}
template<typename T>
void Matrix2D<T>::setColumn(int c) { this->m_column = c; }
template<typename T>
void Matrix2D<T>::setRow(int r) { this->m_row = r; }
template<typename T>
void Matrix2D<T>::setData(int *d) { this->m_data = d; }
template<typename T>
Matrix2D<T> &Matrix2D<T>::add(Matrix2D &m) {
  for (int i = 0; i < m_size; i++) {
    int *data = m.getData();
    if (data)
      m_data[i] += data[i];
  }
  return *this;
}
template<typename T>
Matrix2D<T>::Matrix2D(Matrix2D &m) {
  if (this != &m) {
    if (m.getData()) {
      m_data = m.getData();
      m_column = m.getColumn();
      m_row = m.getRow();
      m_size = m_column * m_row;
    }
    m.setData(nullptr);
    m.setColumn(0);
    m.setRow(0);
    m.setSize(0);
  }
}
template<typename T>
Matrix2D<T>::Matrix2D(initializer_list<T> data,int row,int col){
  m_row = row;
  m_column = col;
  m_size = row*col;
  m_data = new T[m_size]{};
  int index = 0;
  for(auto e:data){
    m_data[index++] = e;
  }
}
template<typename T>
Matrix2D<T> &Matrix2D<T>::operator+(Matrix2D &m) {
  this->add(m);
  return *this;
}
template<typename T>
Matrix2D<T> &Matrix2D<T>::operator-(Matrix2D &m) {
  this->sub(m);
  return *this;
}
template<typename T>
Matrix2D<T> &Matrix2D<T>::operator*(Matrix2D &m) {
  this->dot(m);
  return *this;
}
template<typename T>
Matrix2D<T> &Matrix2D<T>::operator/(Matrix2D &m) {
  this->div(m);
  return *this;
}
template<typename T>
bool Matrix2D<T>::operator==(Matrix2D &m) {
  if (!this->illegal(m))
    return false;
  for (int i = 0; i < m.getSize(); i++) {
    if (this->getData()[i] != m.getData()[i]) {
      return false;
    }
  }
  return true;
}
template<typename T>
bool Matrix2D<T>::illegal(Matrix2D &m) {
  return m.getRow() == this->getRow() && m.getColumn() == this->getColumn() &&
         m.getData();
}
template<typename T>
Matrix2D<T> &Matrix2D<T>::sub(Matrix2D &m) {
  if (this->illegal(m)) {
    T *data = this->getData();
    int column = this->getColumn();
    int row = this->getRow();
    for (int r = 0; r < row; r++) {
      for (int c = 0; c < column; c++) {
        data[r * column + c] -= m.getData()[r * column + c];
      }
    }
  }
  return *this;
}
template<typename T>
Matrix2D<T> &Matrix2D<T>::dot(Matrix2D &m) {
  if (this->illegal(m)) {
    T *data = this->getData();
    int row = this->getRow();
    int column = this->getColumn();
    for (int r = 0; r < row; r++) {
      for (int c = 0; c < column; c++) {
        data[r * column + c] *= m.getData()[r * column + c];
      }
    }
  }
  return *this;
}
template<typename T>
Matrix2D<T> &Matrix2D<T>::div(Matrix2D &m) {
  if (this->illegal(m)) {
    T *data = this->getData();
    int row = this->getRow();
    int column = this->getColumn();
    for (int r = 0; r < this->getRow(); r++) {
      for (int c = 0; c < this->getColumn(); c++) {
        int *subData = m.getData();
        if (subData[r * column + c] == 0)
          throw "Division by zero condition !";
        data[r * column + c] /= subData[r * column + c];
      }
    }
  }
  return *this;
}
template<typename T>
Matrix2D<T> &Matrix2D<T>::matmul(Matrix2D &m) {
  if (this->illegal(m)) {
    T *data = this->getData();
    int *mat_data = m.getData();
    int column = this->getColumn();
    int row = this->getRow();
    T *result = new int[row * column]();
    for (int r = 0; r < row; r++) {
      int res = 0;
      for (int k = 0; k < column; k++) {
        for (int c = 0; c < column; c++) {
          res += data[r * column + c] * mat_data[r * column + k];
        }
        result[r * column + k] = res;
      }
    }
    if (this->m_data) {
      delete[] m_data;
    }
    this->setData(result);
  }
  return *this;
}
template<typename T>
Matrix2D<T> &Matrix2D<T>::scale(float scale,T bias){
  if(this->getData()){
    int size = this->getSize();
    for(int i=0;i<size;i++){
      T scaleValue = this->getData()[i]*scale+bias;
      this->getData()[i] = static_cast<T>(scaleValue);
    }
  }
  return *this;
}

